﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace FirmaKurierska.Models
{
    public class Bazy
    {
        public int id;
        public List<Paczka> lista = new List<Paczka>();

        public Bazy(int ident)
        {
            this.id = ident;
        }
    }


    public class OperacjeWielobazowe
    {
        public int[,] Graph = new int[1500, 1500];
        private int INF = 99999;
        List<Centrale> centrale = new List<Centrale>();
        List<Adresat> adresaci = new List<Adresat>();

        public int[] GetPath(int SRC, int DEST)
        {
            int graphSize = Graph.GetLength(0);
            int[] dist = new int[graphSize];
            int[] prev = new int[graphSize];
            int[] nodes = new int[graphSize];

            for (int i = 0; i < dist.Length; i++)
            {
                dist[i] = prev[i] = INF;
                nodes[i] = i;
            }

            dist[SRC] = 0;
            do
            {
                int smallest = nodes[0];
                int smallestIndex = 0;
                for (int i = 1; i < graphSize; i++)
                {
                    if (dist[nodes[i]] < dist[smallest])
                    {
                        smallest = nodes[i];
                        smallestIndex = i;
                    }
                }
                graphSize--;
                nodes[smallestIndex] = nodes[graphSize];

                if (dist[smallest] == INF || smallest == DEST)
                    break;

                for (int i = 0; i < graphSize; i++)
                {
                    int v = nodes[i];
                    int newDist = dist[smallest] + Graph[smallest, v];
                    if (newDist < dist[v])
                    {
                        dist[v] = newDist;
                        prev[v] = smallest;
                    }
                }
            } while (graphSize > 0);
            return ReconstructPath(prev, SRC, DEST);
        }



        public int[] ReconstructPath(int[] prev, int SRC, int DEST)
        {
            int[] ret = new int[prev.Length];
            int currentNode = 0;
            ret[currentNode] = DEST;
            while (ret[currentNode] != INF && ret[currentNode] != SRC)
            {
                ret[currentNode + 1] = prev[ret[currentNode]];
                currentNode++;
            }
            if (ret[currentNode] != SRC)
                return null;
            int[] reversed = new int[currentNode + 1];
            for (int i = currentNode; i >= 0; i--)
                reversed[currentNode - i] = ret[i];
            return reversed;
        }

        public int GetLength(int[] reversed)
        {
            int length = 0;
            for (int i = 0; i < reversed.GetLength(0) - 1; i++)
            {
                length = length + Graph[reversed[i], reversed[i + 1]];
            }
            return length;
        }


        public int ToBase(Paczka paczka, List<Centrale> centrale, List<Adresat> adresaci, int ACTUAL_BASE, int DEST)
        {
            int destination_base = 0;
            int min_length = 99999;
            Lokalizacja l = new Lokalizacja();
            Adresat adr = new Adresat();

            foreach (var c in centrale)
            {
                adr = jakiAdr(adresaci, paczka);
                int odleg = l.wyliczOdleglosc(c.ulica, Convert.ToInt32(c.nrDomu), c.Miejscowosc, adr.ulica,
                    adr.nr_domu, adr.miasto);
                if (odleg < min_length)
                {
                    min_length = odleg;
                    destination_base = c.Id;
                }
            }

            if (ACTUAL_BASE != destination_base)
            {
                int pa = l.wyliczOdleglosc(centrale[ACTUAL_BASE].ulica, Convert.ToInt32(centrale[ACTUAL_BASE].nrDomu)
                    , centrale[ACTUAL_BASE].Miejscowosc, adresaci[paczka.adresat_id].ulica,
                    adresaci[paczka.adresat_id].nr_domu, adresaci[paczka.adresat_id].miasto);
                int pb = l.wyliczOdleglosc(centrale[ACTUAL_BASE].ulica, Convert.ToInt32(centrale[ACTUAL_BASE].nrDomu)
                    , centrale[ACTUAL_BASE].Miejscowosc, centrale[destination_base].ulica
                    , Convert.ToInt32(centrale[destination_base].nrDomu), centrale[destination_base].Miejscowosc);

                if (pa < pb)
                {
                    destination_base = ACTUAL_BASE;
                }
            }
            return destination_base;
        }

        public Adresat jakiAdr(List<Adresat> ladr, Paczka del)
        {
            Adresat pom = null;
            foreach (Adresat xx in ladr)
                if (xx.aid == del.centrala_id)
                {
                    pom = xx;
                    break;
                }
            return pom;
        }


        public void przelicz()
        {
            var p = new OperacjeWielobazowe();
            var database = new PlacowkaEntities();

            var deliveries = new List<Paczka>();
            foreach (Paczka pa in database.Paczka.ToList())
                if (pa.status == 3)
                    deliveries.Add(pa);

            foreach (Centrale c in database.Centrale.ToList())
                centrale.Add(c);

            foreach (Adresat a in database.Adresat.ToList())
                adresaci.Add(a);

            var all_cars = new List<Samochod>();
            foreach (Samochod s in database.Samochod.ToList())
                if (s.obciazenie == 0)
                    all_cars.Add(s);

            //cars - lista zawierajaca listy samochodow w kazdej bazie
            var cars = new List<List<Samochod>>();
            for (int kk = 0; kk < centrale.Count; kk++)
            {
                var numbers = new List<Samochod>();
                foreach (Samochod s in all_cars)
                    if (kk == Convert.ToInt16(s.centrala_id))
                    {
                        numbers.Add(s);
                    }
                cars.Add(numbers);
            }

            var all_lbaz = new List<Bazy>();
            foreach (var ce in centrale)
                all_lbaz.Add(new Bazy(ce.Id));

            var lbaz = new Bazy[centrale.Count, centrale.Count];
            // tablica zawierajaca info o transporcie paczek
            for (int cou = 0; cou < centrale.Count; cou++)
                for (int cou2 = 0; cou2 < centrale.Count; cou2++)
                    lbaz[cou, cou2] = new Bazy(cou2);

            create_table(deliveries);

            foreach (Paczka d in deliveries)
            {
                int result = p.ToBase(d, centrale, adresaci, (int)d.centrala_id, d.adresat_id);
                lbaz[(int)d.centrala_id, result].lista.Add(d);
            }

            int[,] l = new int[centrale.Count, centrale.Count];  //ile paczek do danej bazy
            int[,] iledo = new int[centrale.Count, centrale.Count];  //ile aut do danej bazy

            for (int cou = 0; cou < centrale.Count; cou++)
                for (int cou2 = 0; cou2 < centrale.Count; cou2++)
                {
                    l[cou, cou2] = lbaz[cou, cou2].lista.Count;
                    iledo[cou, cou2] = (int)Math.Ceiling((double)l[cou, cou2] / all_cars[0].ladownosc);
                    //Console.WriteLine("z " + cou + " do " + cou2 + " " + l[cou, cou2] + " " + iledo[cou, cou2]);
                }

            List<Paczka> wyslane = new List<Paczka>();

            for (int cou = 0; cou < centrale.Count; cou++)
                for (int cou2 = 0; cou2 < centrale.Count; cou2++)
                    if (cou != cou2)
                    {
                        if (iledo[cou, cou2] > 0)
                            for (int cnt = 0; cnt < iledo[cou, cou2]; cnt++)
                            {
                                if (cars[cou].Count > 0)
                                {
                                    while (cars[cou].First().obciazenie < cars[cou].First().ladownosc)
                                    {
                                        if (lbaz[cou, cou2].lista.Count == 0)
                                            break;
                                        Paczka package = lbaz[cou, cou2].lista.First();
                                        package.kierowca_id = cars[cou].First().sid;
                                        package.centrala_id = cou2;
                                        package.status = 4;
                                        cars[cou].First().obciazenie++;
                                        wyslane.Add(package);
                                        lbaz[cou, cou2].lista.RemoveAt(0);

                                    }
                                    cars[cou].RemoveAt(0);
                                }
                            }
                    }

            for (var pa = 0; pa < wyslane.Count; pa++)
                for (var d = 0; d < deliveries.Count; d++)
                    if (wyslane[pa] == deliveries[d])
                        deliveries[d] = wyslane[pa];

            database.SaveChanges();

        }


        void create_table(List<Paczka> deliv)
        {
            Lokalizacja lok = new Lokalizacja();
            for (int i = 0; i < deliv.Count; i++)
            {
                Graph[i, i] = 0;
                for (int j = i + 1; j < deliv.Count; j++)
                {
                    Graph[i, j] = lok.wyliczOdleglosc(deliv[i].Adresat.ulica,
                        deliv[i].Adresat.nr_domu, deliv[i].Adresat.miasto,
                        deliv[j].Adresat.ulica, deliv[j].Adresat.nr_domu,
                        deliv[j].Adresat.miasto);
                    Graph[j, i] = Graph[i, j];
                }
            }
        }
    }

}